package org.xlp.scanner.pkg;

import org.xlp.assertion.AssertUtils;
import org.xlp.scanner.annotation.Scanner;
import org.xlp.utils.XLPStringUtil;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * {@link org.xlp.scanner.annotation.Scanner} 注解扫描解析器
 */
public class ScannerAnnotationParser extends ClassPathPkgScanner implements ConfigurationScannerPkg{

    /**
     * 类加载器
     */
    private ClassLoader classLoader;

    /**
     * 要解析的配置类
     */
    private Class<?> scannerClass;

    /**
     * Scanner
     */
    private final Scanner scanner;

    /**
     * 包过滤器
     */
    private Filter filter;

    /**
     * 构造函数
     * @param classLoader 类加载器
     * @param scannerClass 要解析的配置类
     * @throws NullPointerException 假如第二个参数为null，则抛出该异常
     */
    public ScannerAnnotationParser(ClassLoader classLoader, Class<?> scannerClass) {
        super(classLoader);
        setScannerClass(scannerClass);
        this.scanner = scannerClass.getAnnotation(Scanner.class);
        this.classLoader = classLoader;
    }

    /**
     * 构造函数
     * @param scannerClass 要解析的配置类
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    public ScannerAnnotationParser(Class<?> scannerClass) {
        this((ClassLoader) null, scannerClass);
    }

    /**
     * 构造函数
     *
     * @param filter 过滤器
     * @param scannerClass 要解析的配置类
     * @throws NullPointerException 假如第二个参数为null，则抛出该异常
     */
    public ScannerAnnotationParser(Filter filter, Class<?> scannerClass) {
        super(filter);
        setScannerClass(scannerClass);
        this.scanner = scannerClass.getAnnotation(Scanner.class);
        this.filter = filter;
    }

    /**
     * 
     * @param scannerClass 要解析的配置类
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
	private void setScannerClass(Class<?> scannerClass) {
		AssertUtils.isNotNull(scannerClass, "scannerClass parameter must not be null!");
        this.scannerClass = scannerClass;
	}

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    public Class<?> getScannerClass() {
        return scannerClass;
    }

    /**
     * 获取要解析的类型
     *
     * @return
     */
    @Override
    public Class<?> getParseClass() {
        return scannerClass;
    }

    /**
     * 扫描指定包下的class文件
     *
     * @return 返回指定包下的class全称集合
     * @throws IOException 假如扫描失败，则抛出该异常
     */
    @Override
    public Set<String> scanner() throws IOException {
        if (scanner == null) return scanner(XLPStringUtil.EMPTY, filter);
        //解析Scanner注解
        String[] packages = scanner.value();

        Set<String> basePackages = new HashSet<>();
        if (packages.length == 0){
            Class<?>[] classes = scanner.basePackageByClasses();
            for (Class<?> c : classes) {
                if (c == null) continue;
                basePackages.add(c.getPackage().getName());
            }
            packages = basePackages.toArray(new String[0]);
        }
        if (packages.length == 0){
            String[] ss = scanner.basePackageByClassName();
            for (String s : ss) {
               if(XLPStringUtil.isEmpty(s)) continue;
               int index = s.lastIndexOf(".");
               basePackages.add(index < 0 ? XLPStringUtil.EMPTY : s.substring(0, index));
            }
            packages = basePackages.toArray(new String[0]);
        }
        //扫描出的类集合
        Set<String> classes = new HashSet<>();
        if (packages.length == 0){
            classes = scanner(this.scannerClass.getPackage().getName(), filter);
        } else {
            for (String pkg : packages) {
                classes.addAll(scanner(pkg, filter));
            }
        }
        String[] ignores = scanner.ignorePackage();

        Set<String> filterClass = new HashSet<>();
        //去除不符合的类
        classes.forEach(c -> {
            for (String ignore : ignores) {
                if (c.startsWith(ignore)) {
                    filterClass.add(c);
                    break;
                }
            }
        });
        //移除要过滤的类
        classes.removeAll(filterClass);

        //转换成不可修改的集合
        return Collections.unmodifiableSet(classes);
    }
}
